#pragma once
#include <cstdio>
#include <functional>
#include <sstream>
#include <string>

#define LOG(fmt, ...) printf(   \
    "\033[31m%s\033[0m"         \
    "{\033[33m%d\033[0m}: "     \
    "\033[32m" fmt "\033[0m\n", \
    __func__,                   \
    __LINE__, ##__VA_ARGS__)

class Logger : public std::stringstream {
public:
    using LoggerOutFunc = Logger &(*)(Logger &);

    Logger(const char *file, const char *func, int line, LoggerOutFunc out);
    ~Logger();

    const char *GetFile();
    const char *GetFunc();
    const int GetLine();

private:
    const char *file_ = nullptr;
    const char *func_ = nullptr;
    int line_ = -1;
    LoggerOutFunc out_ = nullptr;
};

Logger &Printf(Logger &logger);
Logger &OuterPrintf(Logger &logger);
Logger &CodePrintf(Logger &logger);
Logger &InnerPrintf(Logger &logger);
Logger &HorizontalLine(Logger &logger);
Logger &ValuePrintf(Logger &logger);

#define LOGGER() Logger(__FILE__, __func__, __LINE__, Printf)
#define OUTER_LOGGER() Logger(__FILE__, __func__, __LINE__, OuterPrintf)
#define CODE_LOGGER(...) Logger(__FILE__, __func__, __LINE__, CodePrintf) << #__VA_ARGS__; __VA_ARGS__
#define INNER_LOGGER() Logger(__FILE__, __func__, __LINE__, InnerPrintf)
#define HORIZONTAL_LINE() Logger(__FILE__, __func__, __LINE__, HorizontalLine)
#define VALUE_LOGGER(val) Logger(__FILE__, __func__, __LINE__, ValuePrintf) << #val << ": " << val
